﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// This calculates an integral image, to get a correct result the picture has to be in grayscale
    /// But you might also calculate the integral from a colored picture
    /// </summary>
    public class IntegralImage : BaseImageFilter
    {
        protected PIXEL_MODE m_PIXEL_MODE = PIXEL_MODE.COLOR_UINT;

        /// <summary>
        /// Constructor
        /// </summary>
        public IntegralImage()
        {
        }

        /// <summary>
        /// used to clone this instance
        /// </summary>
        /// <param name="_integralImage"></param>
        internal IntegralImage(IntegralImage _integralImage)
        {
            m_PIXEL_MODE = _integralImage.m_PIXEL_MODE;
        }

        /// <summary>
        /// Gets or sets pixel mode
        /// </summary>
        public PIXEL_MODE MODE
        {
            get { return m_PIXEL_MODE; }
            set { m_PIXEL_MODE = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            UnsafeBitmap pintegral_image;
            //copy pimage to pintegral_image

            PIXEL_MODE MODE = m_PIXEL_MODE;
            UnsafeBitmap v = _bitmap;

            int i = 0;
            int j = 0;

            for (i = 1; i < nHeight; i++)
            {
                v[0, i, MODE] += v[0, i - 1, MODE];
            }
            for (j = 1; j < nWidth; j++)
            {
                v[j, 0, MODE] += v[j - 1, 0, MODE];
            }

            int nSum = 0;
            for (i = 1; i < nHeight; i++)
            {
                for (j = 1; j < nWidth; j++)
                {
                    nSum = (v[j - 1, i, MODE] + v[j, i - 1, MODE]) - v[j - 1, i - 1, MODE];
                    v[j, i, MODE] += nSum;
                }
            }
            return _bitmap;
        }


        /// <summary>
        /// Calculate sum of pixels in the specified rectangle.
        /// </summary>
        /// 
        /// <param name="x1">X coordinate of left-top rectangle's corner.</param>
        /// <param name="y1">Y coordinate of left-top rectangle's corner.</param>
        /// <param name="x2">X coordinate of right-bottom rectangle's corner.</param>
        /// <param name="y2">Y coordinate of right-bottom rectangle's corner.</param>
        /// 
        /// <returns>Returns sum of pixels in the specified rectangle.</returns>
        /// 
        /// <remarks><para>Both specified points are included into the rectangle calculation rectangle.</para></remarks>
        /// 
        public uint GetRectangleSum(UnsafeBitmap integralImage, uint x1, uint y1, uint x2, uint y2)
        {
            int nWidth = integralImage.Width;
            int nHeight = integralImage.Height;
            // check if requested rectangle is out of the image
            if ((x2 < 0) || (y2 < 0) || (x1 >= nWidth) || (y1 >= nHeight))
                return 0;

            if (x1 < 0) x1 = 0;
            if (y1 < 0) y1 = 0;

            x2++;
            y2++;

            if (x2 > nWidth) x2 = (uint)nWidth;
            if (y2 > nHeight) y2 = (uint)nHeight;

            return integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2];
        }

        /// <summary>
        /// Calculate mean value of pixels in the specified rectangle.
        /// </summary>
        /// 
        /// <param name="x1">X coordinate of left-top rectangle's corner.</param>
        /// <param name="y1">Y coordinate of left-top rectangle's corner.</param>
        /// <param name="x2">X coordinate of right-bottom rectangle's corner.</param>
        /// <param name="y2">Y coordinate of right-bottom rectangle's corner.</param>
        /// 
        /// <returns>Returns mean value of pixels in the specified rectangle.</returns>
        /// 
        /// <remarks>Both specified points are included into the rectangle calculation rectangle.</remarks>
        /// 
        public float GetRectangleMean(UnsafeBitmap integralImage, uint x1, uint y1, uint x2, uint y2)
        {
            int nWidth = integralImage.Width;
            int nHeight = integralImage.Height;
            // check if requested rectangle is out of the image
            if ((x2 < 0) || (y2 < 0) || (x1 >= nWidth) || (y1 >= nHeight))
                return 0;

            if (x1 < 0) x1 = 0;
            if (y1 < 0) y1 = 0;

            x2++;
            y2++;

            if (x2 > nWidth) x2 = (uint)nWidth;
            if (y2 > nHeight) y2 = (uint)nHeight;

            // return sum divided by actual rectangles size
            return (float)((double)(integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]) /
                (double)((x2 - x1) * (y2 - y1)));
        }

        /// <summary>
        /// Clones this instance
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new IntegralImage(this);
        }
    }
}
